require 'fileutils'
require 'ftools'
require 'tmpdir'
require 'timeout'
require 'find'
require 'differ'

class UploadController < ApplicationController
  before_filter :login_required
  attr_accessor :info
  def index
  end

  def msg(msg)
    render :juggernaut => {:type => :send_to_clients, 
      :client_ids => [session[:user].id]} do |page|
      page.replace_html "msg",msg
    end
  end

  def judge(dir,command,test_ext,res_ext)
    Dir.chdir(dir)
    testcases=Dir.glob("*"+test_ext)
    score=0
    round=1
    puts "Test start!"
    has_script = File.exists? "hook.rb"
    code = ""
    if has_script
      puts "found script!"
      File.open "hook.rb" do |f|
        code=f.readlines.to_s
      end
      hooks=eval code
      pre,post=hooks
      puts "get hooks"
    end
    testcases.each do |fname|
      puts "Test case #{round}:#{fname}"
      result_file=File.basename(fname,test_ext) + res_ext
      result=nil
      res=""
      begin
        result=File.open result_file
        res=result.read.gsub!(/[\n \t\r]/,"")
      rescue
        result=nil
        res="NO_SUCH_FILE!"
      ensure
        if (not result.nil?) then result.close end
      end
      if(system("doalarm 10 #{command} "+fname+" >output") == true) 
      then 
        File.open("output") do |ofile|
          submit=ofile.read.gsub!(/[\n \t\r]/,"")
          if(has_script) then 
            submit=pre.call(submit)
            puts "submit after hook:#{submit}"
            res=pre.call(res)
            puts "res    after hook:#{res}"
          end
          if(has_script) then 
            if(post.call(submit,res)) then
              score=score+1
              puts "Pass"
            else
              @info << "Test case #{round} Fails<br/>"
            end
          else 
            if(submit==res) then 
              score=score+1 
              puts "Pass"
            else
              puts "Fail: requires '#{res}' but actually '#{submit}'"
              diff = Differ.diff(res,submit).to_s
              puts "Diff:#{diff}"
              @info << "Test case #{round} Fails\n#{diff}<br/>"
            end
          end
        end
      else
        @info << "Test case #{round} fails with exception<br/>"
      end
      round=round+1
    end 
    puts "Score:#{score}"
    score

  end

  def prepare(name,lab)
    if((name.include? "tar.gz") or (name.include? ".tgz")) then
      system("tar -zxvf #{name}")
    else if (name.include? ".zip") then 
           system("unzip #{name}")
         else if(name.include? ".rar") then 
                system("unrar x #{name}")
              end
         end
    end      
    
    puts "Extraction done!"

    msg "compiling..."

    mlb_path=nil
    puts "try to find mlb file"
    Find.find(Dir.pwd) do |path|
      if FileTest.directory?(path)
        next
      else
        if File.basename(path).include?("#{lab.execfile}.mlb")
          mlb_path=File.dirname(path)
          puts "mlb_path:#{mlb_path}"
          Find.prune
        end
      end
    end

    if(mlb_path.nil?) 
      false
    else
      Dir.chdir(mlb_path)
      FileUtils.rm_f Dir.glob("*.#{lab.ext}")
      suite_path=ENV['suites_base']+"#{lab.id}/"
      Dir.foreach(suite_path) do |fn|
        if(fn != "." and fn !="..") then 
          puts fn
          File.copy(suite_path+fn,Dir.pwd)
        end
      end
      status=system("doalarm 60 mlton #{lab.execfile}.mlb >cstat 2>&1")
      @info=File.read("cstat")
      @info<< "<br/>"
      status and File.exist? lab.execfile
    end
  end

  def uploadFile
    @info = ""
    msg "uploading..."
    path = ENV['save_path'] + current_user.ident
    if(not File.exist? path) then 
      Dir.chdir(ENV['save_path'])
      Dir.mkdir(current_user.ident)
    end
    post = DataFile.save( params[:upload],path)
    success = false
    Dir.mktmpdir do |tmp_path|
      if(post) then 
        link = post.split('public').pop
        puts "Upload done!"
        msg "extracting..."
        lab = Lab.find(params[:lab_id]) 
        FileUtils.cp(post,tmp_path + "/")
        Dir.chdir(tmp_path)
        name=File.basename post
        new_score=0
        if(prepare(name,lab)) 
          puts "Prepare done!"
          msg "running test..."
          new_score = judge(Dir.pwd, "./"+lab.execfile ,".#{lab.ext}",".res")
        end
        id=params[:lab_id].to_i
        uid=current_user.id
        score=Score.find_by_user_id_and_lab_id(uid,id)
        if(score.nil?) then 
          score=Score.new
          score.lab_id=id
          score.user_id=uid
          score.res=new_score
          score.store=link
          score.save
        else
          score.res=new_score
          score.store=link
          score.save
        end
        puts "Info=#{@info}"
        if(@info!="") then 
          flash[:warning]= @info[0..512]
        else
          flash[:message]="Your score is updated!"
        end
      else
        flash[:warning]="Must upload a zip, rar, tgz or tar.gz file!"
      end
    end
    
    
    redirect_to :controller => 'user',:action => 'home'
   
    
  end


end
